#include "hardware/hal.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "freertos/FreeRTOS.h"
ClockHAL hal;
static bool _pmu_irq = false;
static bool _sdchanged = false;
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf;
static lv_color_t *buf2;

static void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);
    hal.display.waitDMA();
    hal.display.startWrite();
    hal.display.setAddrWindow(area->x1, area->y1, w, h);
    hal.display.writePixelsDMA((uint16_t *)&color_p->full, w * h, true);
    hal.display.endWrite();
    lv_disp_flush_ready(disp);
}

static void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
    TS_Point p = hal.touch.getPoint();
    if (p.z != 0)
    {
        // Retrieve a point
        // Serial.print("X: ");
        // Serial.print(p.x);
        // Serial.print(" Y: ");
        // Serial.println(p.y);
        // Serial.print(" Z: ");
        // Serial.println(p.z);
        data->state = LV_INDEV_STATE_PR;
        data->point.x = p.y;
        data->point.y = screenHeight - p.x;
        hal.release_time = millis();
    }
    else
        data->state = LV_INDEV_STATE_REL;
}

static void lvgl_init()
{
    buf = (lv_color_t *)ps_malloc(screenWidth * screenHeight * sizeof(lv_color_t));
    buf2 = (lv_color_t *)ps_malloc(screenWidth * screenHeight * sizeof(lv_color_t));
    lv_init();
    lv_disp_draw_buf_init(&draw_buf, buf, buf2, screenWidth * screenHeight);

    /*初始化显示*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    /*初始化输入设备*/
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);
}

void ClockHAL::update()
{
    static uint32_t last_time = 0;
    if (DoNotSleep)
        release_time = millis();
    if(USBConnected && DoNotSleepWhenUSB)
        release_time = millis();
    if (millis() - last_time <= 50)
        return;
    last_time = millis();
    xSemaphoreTake(wiremutex, portMAX_DELAY);
    Wire.flush();
#ifdef RTC_IS_DS3231
    rtc.getAll();
#else
    rtc.getTime();
    rtc.getDate();
#endif
    Wire.flush();
    if (_pmu_irq)
    {
        release_time = millis();
        _pmu_irq = 0;
        axp.readIRQ();
        if (axp.isPEKShortPressIRQ())
            axpShortPress = true;
        if (axp.isPEKLongtPressIRQ())
            axpLongPress = true;
        axp.clearIRQ();
    }
    xSemaphoreGive(wiremutex);
    if (_sdchanged)
    {
        delay(20);                       // 去抖动
        if (digitalRead(SD_SENSOR) == 0) // SD 卡插入
        {
            if (hal.mountSD())
            {
                lv_toast("TF卡挂载成功");
                SDMounted = true;
            }
            else
            {
                lv_toast("TF卡已插入但挂载失败");
                SDMounted = false;
            }
        }
        else if (SDMounted)
        {
            lv_toast("TF卡已拔出");
            hal.unmountSD();
            SDMounted = false;
        }
        setMediaPresent(SDMounted);
        _sdchanged = false;
    }
    if (millis() - release_time > autoSleepTime)
    {
        if (canDeepSleep && canDeepSleepFromAlarm)
            deepSleep();
        else
            lightSleep();
    }
}
void ClockHAL::resetTouch()
{
    pinMode(TOUCH_RST, OUTPUT);
    digitalWrite(TOUCH_RST, LOW);
    delay(10);
    digitalWrite(TOUCH_RST, HIGH);
    touch.i2cAddr = GT911_I2C_ADDR_BA;
    touch.fwResolution(240, 320);
}
void ClockHAL::touchSleep()
{
    touch.write(0x8040, 5);
}
void ClockHAL::initHardware()
{
    // 外设初始化
    //  rtc_gpio_deinit((gpio_num_t)RTC_IRQ);
    //  rtc_gpio_deinit((gpio_num_t)AXP192_IRQ);
    pinMode(SD_CS, OUTPUT);
    digitalWrite(SD_CS, HIGH);
    SPIFFS.begin(true);
#ifndef SD_IS_SDMMC
    SDSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, -1);
#endif
    WiFi.mode(WIFI_OFF);
    btStop();
    Wire.begin(I2C_SDA, I2C_SCL);
    Wire1.begin(I2C1_SDA, I2C1_SCL, 400000);
    // 创建互斥锁
    //  SPILock = xSemaphoreCreateMutex();
    wiremutex = xSemaphoreCreateMutex();
    screenMutex = xSemaphoreCreateMutex();

    int ret = axp.begin(Wire, AXP192_SLAVE_ADDRESS);
    if (ret == AXP_FAIL)
    {
        Serial.println("AXP Power begin failed");
        while (1)
            ;
    }

    axp.setVWarningLevel1(3450);
    axp.setVWarningLevel2(3400);
    axp.setPowerDownVoltage(3300);

    //! enable all irq channel
    axp.enableIRQ(AXP202_PEK_SHORTPRESS_IRQ | AXP202_PEK_LONGPRESS_IRQ, true);
    axp.clearIRQ();

    axp.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
    axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
    axp.setPowerOutPut(AXP192_DCDC2, AXP202_OFF);
    axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);

    axp.setShutdownTime(AXP_POWER_OFF_TIME_4S);
    axp.setlongPressTime(AXP_LONGPRESS_TIME_1S);
    axp.setStartupTime(AXP192_STARTUP_TIME_128MS);
    axp.adc1Enable(AXP202_BATT_VOL_ADC1 | AXP202_BATT_CUR_ADC1 | AXP202_ACIN_CUR_ADC1 | AXP202_ACIN_VOL_ADC1 | AXP202_VBUS_CUR_ADC1 | AXP202_VBUS_VOL_ADC1, true);

    if (settings.getBool("settings.encoulomb", false) == true)
    {
        axp.EnableCoulombcounter();
    }
    else
    {
        axp.DisableCoulombcounter();
    }

    axp.setChargingTargetVoltage(AXP202_TARGET_VOL_4_2V);
    axp.setAdcSamplingRate(AXP_ADC_SAMPLING_RATE_100HZ);
    axp.setTSmode(AXP_TS_PIN_MODE_DISABLE);
    axp.setChargeControlCur(settings.getInt("settings.chargecurrent", 7));
    pinMode(AXP192_IRQ, INPUT_PULLUP);
    pinMode(SD_SENSOR, INPUT_PULLUP);
    attachInterrupt(
        AXP192_IRQ, []
        { _pmu_irq = true; },
        FALLING);
    attachInterrupt(
        SD_SENSOR, []
        { _sdchanged = true; },
        CHANGE);
    if (digitalRead(SD_SENSOR) == 0)
    {
        if (mountSD())
            SDMounted = true;
    }
    setupMSC();
    resetTouch();
    display.init();
    display.fillScreen(TFT_BLACK);
    displayOn();
    lvgl_init();
    release_time = millis();
    autoSleepTime = settings.getInt("settings.autosleep", AUTO_SLEEP_TIME_DEFAULT);
    setBrightness(Brightness);
}

bool ClockHAL::mountSD()
{
#ifdef SD_IS_SDMMC
    SDCARD.setPins(SDIO_CLK, SDIO_CMD, SDIO_D0, SDIO_D1, SDIO_D2, SDIO_D3);
    if (!SDCARD.begin())
#else
    if (!SDCARD.begin(SD_CS, SDSPI, 40000000))
#endif
    {
        return false;
    }
    return true;
}

void ClockHAL::unmountSD()
{
    SDCARD.end();
}

void ClockHAL::displayOff(void)
{
    xSemaphoreTake(screenMutex, portMAX_DELAY);
    display.writecommand(0x10);
    touchSleep();
    xSemaphoreGive(screenMutex);
}

void ClockHAL::displayOn(void)
{
    xSemaphoreTake(screenMutex, portMAX_DELAY);
    display.writecommand(0x11);
    resetTouch();
    vTaskDelay(5);
    xSemaphoreGive(screenMutex);
}

void ClockHAL::setBrightness(uint8_t b)
{
    if (b > 100)
        b = 100;
    if (b != 0)
    {
        Brightness = b;
    }
    else
    {
        xSemaphoreTake(hal.wiremutex, portMAX_DELAY);
        axp.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
        xSemaphoreGive(hal.wiremutex);
        return;
    }
    xSemaphoreTake(hal.wiremutex, portMAX_DELAY);
    axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
    axp.setDCDC1Voltage(2480 + (uint16_t)b * 5);
    xSemaphoreGive(hal.wiremutex);
}

void ClockHAL::pre_sleep()
{
    setBrightness(0);
    displayOff();
    detachInterrupt(AXP192_IRQ);
    detachInterrupt(SD_SENSOR);
    xSemaphoreTake(screenMutex, portMAX_DELAY);
    xSemaphoreTake(wiremutex, portMAX_DELAY);
    axp.adc1Enable(AXP202_ACIN_CUR_ADC1 | AXP202_ACIN_VOL_ADC1 | AXP202_VBUS_CUR_ADC1 | AXP202_VBUS_VOL_ADC1 | AXP202_TS_PIN_ADC1 | AXP202_APS_VOL_ADC1, false);
    axp.adc2Enable(AXP202_TEMP_MONITORING_ADC2 | AXP202_GPIO1_FUNC_ADC2 | AXP202_GPIO0_FUNC_ADC2, false);
#ifdef RTC_IS_DS3231
    rtc.checkIfAlarm(1);
#else
    rtc.resetAlarm();
#endif
    xSemaphoreGive(wiremutex);
#ifndef AXP_RTC_INT_BOND
    // rtc_gpio_init((gpio_num_t)RTC_IRQ);
    rtc_gpio_pullup_en((gpio_num_t)RTC_IRQ);
    rtc_gpio_hold_en((gpio_num_t)RTC_IRQ);
#endif
    // rtc_gpio_init((gpio_num_t)AXP192_IRQ);
    rtc_gpio_pullup_en((gpio_num_t)AXP192_IRQ);
    rtc_gpio_hold_en((gpio_num_t)AXP192_IRQ);
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
    gpio_deep_sleep_hold_en();
#ifndef AXP_RTC_INT_BOND
    esp_sleep_enable_ext1_wakeup((uint64_t)1 << (uint64_t)RTC_IRQ, ESP_EXT1_WAKEUP_ALL_LOW);
#endif
    esp_sleep_enable_ext0_wakeup((gpio_num_t)AXP192_IRQ, 0); // 1 = High, 0 = Low
    delay(50);
}

void ClockHAL::post_sleep()
{
#ifndef AXP_RTC_INT_BOND
    rtc_gpio_deinit((gpio_num_t)RTC_IRQ);
#endif
    rtc_gpio_deinit((gpio_num_t)AXP192_IRQ);

    xSemaphoreGive(screenMutex);
    displayOn();
    setBrightness(Brightness);

    xSemaphoreTake(hal.wiremutex, portMAX_DELAY);
    axp.clearIRQ();
    axp.adc1Enable(AXP202_ACIN_CUR_ADC1 | AXP202_ACIN_VOL_ADC1 | AXP202_VBUS_CUR_ADC1 | AXP202_VBUS_VOL_ADC1, true);
    xSemaphoreGive(hal.wiremutex);
    attachInterrupt(
        AXP192_IRQ, []
        { _pmu_irq = true; },
        FALLING);
    attachInterrupt(
        SD_SENSOR, []
        { _sdchanged = true; },
        CHANGE);
    hal.release_time = millis();
}

void ClockHAL::lightSleep()
{
    pre_sleep();
    esp_light_sleep_start();
    post_sleep();
}
void ClockHAL::deepSleep()
{
    pre_sleep();
    esp_deep_sleep_start();
}
/*-------- NTP ----------*/

static const int NTP_PACKET_SIZE = 48;        // NTP time is in the first 48 bytes of message
static byte NTPpacketBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets

// send an NTP request to the time server at the given address
static void sendNTPpacket(IPAddress &address)
{
    // set all bytes in the buffer to 0
    memset(NTPpacketBuffer, 0, NTP_PACKET_SIZE);
    // Initialize values needed to form NTP request
    // (see URL above for details on the packets)
    NTPpacketBuffer[0] = 0b11100011; // LI, Version, Mode
    NTPpacketBuffer[1] = 0;          // Stratum, or type of clock
    NTPpacketBuffer[2] = 6;          // Polling Interval
    NTPpacketBuffer[3] = 0xEC;       // Peer Clock Precision
    // 8 bytes of zero for Root Delay & Root Dispersion
    NTPpacketBuffer[12] = 49;
    NTPpacketBuffer[13] = 0x4E;
    NTPpacketBuffer[14] = 49;
    NTPpacketBuffer[15] = 52;
    // all NTP fields have been given values, now
    // you can send a packet requesting a timestamp:
    hal.Udp.beginPacket(address, 123); // NTP requests are to port 123
    hal.Udp.write(NTPpacketBuffer, NTP_PACKET_SIZE);
    hal.Udp.endPacket();
}
static time_t getNtpTime()
{
    IPAddress ntpServerIP;
    while (hal.Udp.parsePacket() > 0)
        ;
    WiFi.hostByName(CONFIG_NTP_ADDR, ntpServerIP);
    sendNTPpacket(ntpServerIP);
    uint32_t beginWait = millis();
    while (millis() - beginWait < 1500)
    {
        int size = hal.Udp.parsePacket();
        if (size >= NTP_PACKET_SIZE)
        {
            hal.Udp.read(NTPpacketBuffer, NTP_PACKET_SIZE);
            unsigned long secsSince1900;
            secsSince1900 = (unsigned long)NTPpacketBuffer[40] << 24;
            secsSince1900 |= (unsigned long)NTPpacketBuffer[41] << 16;
            secsSince1900 |= (unsigned long)NTPpacketBuffer[42] << 8;
            secsSince1900 |= (unsigned long)NTPpacketBuffer[43];
            return secsSince1900 - 2208988800UL;
        }
    }
    return 0; // return 0 if unable to get the time
}

bool ClockHAL::NTPSync()
{
    time_t tm_now;
    hal.Udp.begin(8888);
    for (uint8_t i = 0; i < 5; ++i)
    {
        delay(10);
        tm_now = getNtpTime();
        if (tm_now != 0 && tm_now != 0xffffffff)
        {
            tm_now += CONFIG_NTP_OFFSET;
            DateTime dt(tm_now);
            tm_now /= 86400;
            uint8_t week = (tm_now + 4) % 7;
            week--;
            if (week > 6)
                week = 6;
#ifdef RTC_IS_DS3231
            week++;
#endif
            if (dt.year() > 2099 || dt.month() > 12 || dt.day() > 31 || dt.hour() > 23 || dt.minute() > 60 || dt.second() > 60)
            {
                tm_now = 0;
                continue;
            }
            xSemaphoreTake(wiremutex, portMAX_DELAY);
            uint8_t j;
            for (j = 0; j < 5; ++j)
            {
                Wire.flush();
#ifdef RTC_IS_DS3231
                hal.rtc.setDate(dt.day());
                hal.rtc.setMonth(dt.month());
                hal.rtc.setYear(dt.year() - 2000);
                hal.rtc.setDoW(week);
                hal.rtc.setHour(dt.hour());
                hal.rtc.setMinute(dt.minute());
                hal.rtc.setSecond(dt.second());
                hal.rtc.getAll();
#else
                hal.rtc.setDate(dt.day(), week, dt.month(), 0, dt.year() - 2000);
                hal.rtc.setTime(dt.hour(), dt.minute(), dt.second());
                hal.rtc.getDate();
                hal.rtc.getTime();
#endif
                if (((dt.year() - 2000) != hal.rtc.year) || (dt.month() != hal.rtc.month) || (dt.day() != hal.rtc.day) || (dt.hour() != hal.rtc.hour) || (dt.minute() != hal.rtc.minute) || (dt.second() != hal.rtc.sec))
                    continue;
                else
                    break;
            }
            xSemaphoreGive(wiremutex);
            if (j == 5)
            {
                tm_now = 0;
                continue;
            }
            break;
        }
        else
        {
            tm_now = 0;
        }
    }
    hal.Udp.stop();
    WiFi.mode(WIFI_OFF);
    if (tm_now == 0)
        return false;
    return true;
}

void ClockHAL::rtcOffsetSecond(int8_t seconds)
{
    xSemaphoreTake(wiremutex, portMAX_DELAY); // 锁住rtc，防止叠加层访问造成微调失败
    uint8_t waituntil;
    uint8_t i;
    if (seconds < -15 || seconds > 15 || seconds == 0)
    {
        xSemaphoreGive(wiremutex);
        return;
    }
    waituntil = hal.rtc.getSecond();
    do
    {
        vTaskDelay(100);
        i = hal.rtc.getSecond();
    } while (i == waituntil);
    if (seconds < 0)
    {
        // 向前偏移秒数
        waituntil = abs(seconds) + 2; //+2防止出问题
        do
        {
            vTaskDelay(10);
            i = hal.rtc.getSecond();
        } while (i <= waituntil);
#ifdef RTC_IS_DS3231
        hal.rtc.setHour(hal.rtc.hour);
        hal.rtc.setMinute(hal.rtc.minute);
        hal.rtc.setSecond(i + seconds);
#else
        hal.rtc.getTime();
        hal.rtc.setTime(hal.rtc.hour, hal.rtc.minute, i + seconds);
#endif
    }
    else
    {
        // 向后偏移秒数
        waituntil = 60 - 2 - seconds; //-2防止出问题
        do
        {
            vTaskDelay(10);
            i = hal.rtc.getSecond();
        } while (i >= waituntil || i == 0);
#ifdef RTC_IS_DS3231
        hal.rtc.setHour(hal.rtc.hour);
        hal.rtc.setMinute(hal.rtc.minute);
        hal.rtc.setSecond(i + seconds);
#else
        hal.rtc.getTime();
        hal.rtc.setTime(hal.rtc.hour, hal.rtc.minute, i + seconds);
#endif
    }
    xSemaphoreGive(wiremutex);
}

void ClockHAL::calibrateTouch()
{
    return;
}
